package org.paboo.chart.datamatrix;

import java.awt.image.BufferedImage;

import org.paboo.Util.ZxingImage;

import com.google.zxing.BarcodeFormat;
import com.google.zxing.Dimension;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.datamatrix.DataMatrixWriter;
import com.google.zxing.datamatrix.encoder.DefaultPlacement;
import com.google.zxing.datamatrix.encoder.ErrorCorrection;
import com.google.zxing.datamatrix.encoder.HighLevelEncoder;
import com.google.zxing.datamatrix.encoder.SymbolInfo;
import com.google.zxing.datamatrix.encoder.SymbolShapeHint;
import com.google.zxing.qrcode.encoder.ByteMatrix;

public class ZxingUtil {

	// Content is must be ISO 8859-1
	public static BufferedImage encode(String content, int width, int height) {
		ByteMatrix matrix = renderResult(content, width, height);
		BitMatrix input = ZxingImage.resultDecodec(matrix, width, height, 0);
		return ZxingImage.toBufferedImage(input, ZxingImage.WHITE, ZxingImage.BLACK);
	}

	private static ByteMatrix renderResult(String contents, int width, int height) {
		SymbolShapeHint shape = SymbolShapeHint.FORCE_SQUARE;
		Dimension minSize = null;
		Dimension maxSize = null;

		// 1. step: Data encodation
		String encoded = HighLevelEncoder.encodeHighLevel(contents, shape, minSize, maxSize);

		SymbolInfo symbolInfo = SymbolInfo.lookup(encoded.length(), shape, minSize, maxSize, true);

		// 2. step: ECC generation
		String codewords = ErrorCorrection.encodeECC200(encoded, symbolInfo);

		// 3. step: Module placement in Matrix
		DefaultPlacement placement = new DefaultPlacement(codewords, symbolInfo.getSymbolDataWidth(),
				symbolInfo.getSymbolDataHeight());
		placement.place();

		// 4. step: low-level encoding
		return encodeLowLevel(placement, symbolInfo);
	}

	private static ByteMatrix encodeLowLevel(DefaultPlacement placement, SymbolInfo symbolInfo) {
		int symbolWidth = symbolInfo.getSymbolDataWidth();
		int symbolHeight = symbolInfo.getSymbolDataHeight();

		ByteMatrix matrix = new ByteMatrix(symbolInfo.getSymbolWidth(), symbolInfo.getSymbolHeight());

		int matrixY = 0;

		for (int y = 0; y < symbolHeight; y++) {
			// Fill the top edge with alternate 0 / 1
			int matrixX;
			if ((y % symbolInfo.matrixHeight) == 0) {
				matrixX = 0;
				for (int x = 0; x < symbolInfo.getSymbolWidth(); x++) {
					matrix.set(matrixX, matrixY, (x % 2) == 0);
					matrixX++;
				}
				matrixY++;
			}
			matrixX = 0;
			for (int x = 0; x < symbolWidth; x++) {
				// Fill the right edge with full 1
				if ((x % symbolInfo.matrixWidth) == 0) {
					matrix.set(matrixX, matrixY, true);
					matrixX++;
				}
				matrix.set(matrixX, matrixY, placement.getBit(x, y));
				matrixX++;
				// Fill the right edge with alternate 0 / 1
				if ((x % symbolInfo.matrixWidth) == symbolInfo.matrixWidth - 1) {
					matrix.set(matrixX, matrixY, (y % 2) == 0);
					matrixX++;
				}
			}
			matrixY++;
			// Fill the bottom edge with full 1
			if ((y % symbolInfo.matrixHeight) == symbolInfo.matrixHeight - 1) {
				matrixX = 0;
				for (int x = 0; x < symbolInfo.getSymbolWidth(); x++) {
					matrix.set(matrixX, matrixY, true);
					matrixX++;
				}
				matrixY++;
			}
		}

		return matrix;
	}

}
